<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>拖拽排序示例</title>
<style>
  ul {
    list-style-type: none;
    padding: 0;
    margin: 0;
  }
  li {
    padding: 10px;
    margin: 5px;
    background-color: #f0f0f0;
    border: 1px solid #ccc;
    cursor: move;
  }
  .placeholder {
    background-color: #e0e0e0;
    border: 1px dashed #ccc;
    padding: 10px;
    margin: 5px;
  }
</style>
</head>
<body>

<ul id="sortable-list">
  <li draggable="true">项目 1</li>
  <li draggable="true">项目 2</li>
  <li draggable="true">项目 3</li>
  <li draggable="true">项目 4</li>
</ul>

<script>
const list = document.getElementById('sortable-list');

let draggingItem = null;
let placeholder = null;

// 拖拽开始事件
list.addEventListener('dragstart', (event) => {
  draggingItem = event.target;
  event.dataTransfer.effectAllowed = 'move';
});

// 拖拽结束事件
list.addEventListener('dragend', (event) => {
  if (placeholder) {
    placeholder.parentNode.removeChild(placeholder);
  }
  draggingItem = null;
  placeholder = null;
});

// 拖拽进入事件
list.addEventListener('dragover', (event) => {
  event.preventDefault();
  const afterElement = getDragAfterElement(event.clientY);
  if (afterElement === draggingItem) {
    return;
  }
  if (placeholder) {
    list.insertBefore(placeholder, afterElement);
  } else {
    list.insertBefore(createPlaceholder(draggingItem), afterElement);
  }
});

// 拖拽离开事件
list.addEventListener('dragleave', (event) => {
  if (placeholder) {
    list.insertBefore(createPlaceholder(draggingItem), draggingItem);
  }
});

// 拖拽放置事件
list.addEventListener('drop', (event) => {
  event.preventDefault();
  const afterElement = getDragAfterElement(event.clientY);
  list.insertBefore(draggingItem, afterElement || null);
  if (placeholder) {
    placeholder.parentNode.removeChild(placeholder);
  }
  draggingItem = null;
  placeholder = null;
});

function createPlaceholder(item) {
  const placeholder = document.createElement('li');
  placeholder.className = 'placeholder';
  return placeholder;
}

function getDragAfterElement(y) {
  const draggableItems = Array.from(list.children).filter(item => item.draggable);
  return draggableItems.reduce((closest, child) => {
    const box = child.getBoundingClientRect();
    const offset = y - box.top - list.offsetTop;
    if (offset < box.height / 2) {
      return closest;
    } else {
      return child;
    }
  }, null);
}
</script>

</body>
</html>